/*-
 * Scala parser driver.
 *
 * Copyright (c) 2010 Alex Suhan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer
 * in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;


public class ScalaParser
{
    private static CommonTokenStream prepareParsing(InputStream is)
    throws IOException
    {
        ANTLRInputStream input = new ANTLRInputStream(is);
        ScalaTreeBuilderLexer lexer = new ScalaTreeBuilderLexer(input);
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        return tokenStream;
    }

    private static CommonTree buildScalaTree(InputStream is)
    throws IOException, RecognitionException
    {
        CommonTokenStream tokenStream = prepareParsing(is);
        ScalaTreeBuilderParser parser = new ScalaTreeBuilderParser(tokenStream);
        ScalaTreeBuilderParser.program_return retVal = parser.program();
        return (CommonTree) retVal.getTree();
    }
    
    private static void buildScalaModel(CommonTree ast, String fileName)
    throws RecognitionException
    {
        CommonTreeNodeStream nodeStream = new CommonTreeNodeStream(ast);
        ScalaMapsBuilder mapsBuilder = new ScalaMapsBuilder(nodeStream);
        mapsBuilder.crtFileName = fileName;
        mapsBuilder.program();
        CommonTreeNodeStream nodeStream2 = new CommonTreeNodeStream(ast);
        ScalaTreeChecker checker = new ScalaTreeChecker(nodeStream2);
        checker.program();
    }
    
    private static void installBasicClasses()
    {
        ScalaFormals noFormals = new ScalaFormals();
        // Object
        ScalaClassInfo objectInfo =
            new ScalaClassInfo(ScalaConstants.NoTypeClassname, null);
        ScalaMethodInfo objectAbort =
            new ScalaMethodInfo(ScalaConstants.NoTypeClassname, noFormals, false);
        objectInfo.putMethod("abort", objectAbort);
        ScalaMethodInfo objectCopy =
            new ScalaMethodInfo(ScalaConstants.ObjectClassname, noFormals, false);
        objectInfo.putMethod("copy", objectCopy);
        ScalaMethodInfo objectTypename =
            new ScalaMethodInfo(ScalaConstants.StringClassname, noFormals, false);
        objectInfo.putMethod("type_name", objectTypename);
        ScalaMapsBuilder.classTable.put(ScalaConstants.ObjectClassname, objectInfo);
        // String
        ScalaClassInfo stringInfo =
            new ScalaClassInfo(ScalaConstants.ObjectClassname, null);
        ScalaFormals stringConcatFormals = new ScalaFormals();
        stringConcatFormals.add(new ScalaFormal("arg", ScalaConstants.StringClassname));
        ScalaMethodInfo stringConcat =
            new ScalaMethodInfo(ScalaConstants.StringClassname, stringConcatFormals, false);
        stringInfo.putMethod("concat", stringConcat);
        ScalaFormals stringSubstrFormals = new ScalaFormals();
        stringSubstrFormals.add(new ScalaFormal("arg1", ScalaConstants.IntegerClassname));
        stringSubstrFormals.add(new ScalaFormal("arg2", ScalaConstants.IntegerClassname));
        ScalaMethodInfo stringSubstr =
            new ScalaMethodInfo(ScalaConstants.StringClassname, stringSubstrFormals, false);
        stringInfo.putMethod("substr", stringSubstr);
        ScalaMethodInfo stringLength =
            new ScalaMethodInfo(ScalaConstants.IntegerClassname, noFormals, false);
        stringInfo.putMethod("length", stringLength);
        ScalaMapsBuilder.classTable.put(ScalaConstants.StringClassname, stringInfo);
        // IO
        ScalaClassInfo ioInfo =
            new ScalaClassInfo(ScalaConstants.ObjectClassname, null);
        ScalaFormals outStringFormals = new ScalaFormals();
        outStringFormals.add(new ScalaFormal("arg", ScalaConstants.StringClassname));
        ScalaMethodInfo outString =
            new ScalaMethodInfo(ScalaConstants.ConsoleClassname, outStringFormals, false);
        ioInfo.putMethod("out_string", outString);
        ScalaFormals outIntFormals = new ScalaFormals();
        outIntFormals.add(new ScalaFormal("arg", ScalaConstants.IntegerClassname));
        ScalaMethodInfo outInt =
            new ScalaMethodInfo(ScalaConstants.ConsoleClassname, outIntFormals, false);
        ioInfo.putMethod("out_int", outInt);
        ScalaMethodInfo inString =
            new ScalaMethodInfo(ScalaConstants.StringClassname, noFormals, false);
        ioInfo.putMethod("in_string", inString);
        ScalaMethodInfo inInt =
            new ScalaMethodInfo(ScalaConstants.IntegerClassname, noFormals, false);
        ioInfo.putMethod("in_int", inInt);
        ScalaMapsBuilder.classTable.put(ScalaConstants.ConsoleClassname, ioInfo);
        // Int
        ScalaMapsBuilder.classTable.put(
            ScalaConstants.IntegerClassname,
            new ScalaClassInfo(ScalaConstants.ObjectClassname, null));
        // Bool
        ScalaMapsBuilder.classTable.put(
            ScalaConstants.BooleanClassname,
            new ScalaClassInfo(ScalaConstants.ObjectClassname, null));
    }

    public static void main(String[] args) throws IOException, RecognitionException
    {
        installBasicClasses();
        CommonTree[] rootNodes = new CommonTree[args.length];
        for (int idx = 0; idx < args.length; idx++)
        {
            FileInputStream fis = new FileInputStream(args[idx]);
            ScalaTreeChecker.addFilenameToSymtable(args[idx]);
            rootNodes[idx] = buildScalaTree(fis);
            fis.close();
        }
        for (int idx = 0; idx < args.length; idx++)
            buildScalaModel(rootNodes[idx], args[idx]);
    }
}
